﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;


namespace Tum.CollabXT.VModellXT.Tailoring
{
    /// <summary>
    /// This class contains the final tailoring. Furthermore this class contains
    /// the logic to export a VMP-file, which allows using the V-Modell XT Project Assistant.
    /// </summary>
    public class Tailoring
    {
        public string ProjectName
        {
            get;
            private set;
        }

        public VModellXT Model
        {
            get;
            private set;
        }

        public ProcessModule[] ProcessModules
        {
            get;
            private set;
        }

        /// <summary>
        /// Project type chosen for this tailoring.
        /// </summary>
        public ProjectType ProjType
        {
            get;
            private set;
        }

        /// <summary>
        /// Project type variant chosen for this tailoring.
        /// </summary>
        public ProjectTypeVariant ProjTypeVariant
        {
            get;
            private set;
        }

        /// <summary>
        /// Project characteristics and their belonging values set for this tailoring.
        /// </summary>
        public KeyValuePair<ProjectCharacteristic, ProjectCharacteristicValue>[] ProjCharacteristics
        {
            get;
            private set;
        }

        /// <summary>
        /// Process execution modules included in this tailoring.
        /// </summary>
        public ProcessExecutionModule[] ProcessExecutionModules
        {
            get;
            private set;
        }

        /// <summary>
        /// Process execution module specifications included in this tailoring.
        /// </summary>
        public ProcessExecutionModuleSpecification[] ProcessExecutionModuleSpecifications
        {
            get;
            private set;
        }


        internal static Tailoring Build(TailoringBuilder builder)
        {
            if (builder == null)
                throw new ArgumentNullException();
            if(builder.ProjType == null)
                throw new ArgumentException("ProjType not specified.");

            //Create new tailoring
            var tailoring = new Tailoring();
            tailoring.Model = builder.Model;
            tailoring.ProjType = builder.ProjType;
            tailoring.ProjTypeVariant = builder.ProjTypeVariant;
            tailoring.ProjectName = string.IsNullOrEmpty(builder.ProjName) ? string.Empty : builder.ProjName;


            //Check data validity
            CheckBuilderValidity(builder);

            //Add missing characteristics using default values
            var characteristicsList = new List<KeyValuePair<ProjectCharacteristic, ProjectCharacteristicValue>>(builder.ProjCharacteristics);
            IEnumerable<ProjectCharacteristic> allCharacteristics = tailoring.ProjType.ProjectCharacteristics;
            if(builder.ProjTypeVariant != null)
                allCharacteristics = builder.ProjTypeVariant.ProjectCharacteristics.Union(allCharacteristics);
            foreach (var characteristic in allCharacteristics)
            {
                bool foundChar = false;
                foreach (var existingCharacteristic in characteristicsList)
                {
                    if (characteristic.Id.Equals(existingCharacteristic.Key.Id))
                    {
                        foundChar = true;
                        break;
                    }
                }
                if (!foundChar)
                    characteristicsList.Add(new KeyValuePair<ProjectCharacteristic, ProjectCharacteristicValue>(characteristic, characteristic.DefaultValue));
            }
            tailoring.ProjCharacteristics = characteristicsList.ToArray();


            //Build process modules list, process execution modules list and process execution module specifications list
            var processModulesDict = new Dictionary<string, ProcessModule>();
            var processExecutionModulesDict = new Dictionary<string, ProcessExecutionModule>();
            var processExecutionModuleSpecsDict = new Dictionary<string, ProcessExecutionModuleSpecification>();

            foreach (var pm in builder.ProjType.ProcessModules)
                processModulesDict[pm.Id] = pm;
            foreach (var pems in builder.ProjType.ProcessExecutionModuleSpecs)
                processExecutionModuleSpecsDict[pems.Id] = pems;

            if (builder.ProjTypeVariant != null)
            {
                foreach (var pm in builder.ProjTypeVariant.ProcessModules)
                    processModulesDict[pm.Id] = pm;

                foreach (var pem in builder.ProjTypeVariant.ProcessExecutionModules)
                    processExecutionModulesDict[pem.Id] = pem;

                foreach (var pems in builder.ProjTypeVariant.ProcessExecutionModuleSpecs)
                    processExecutionModuleSpecsDict[pems.Id] = pems;

                foreach (var characteristic in tailoring.ProjCharacteristics)
                {
                    foreach (var pm in characteristic.Value.ProcessModules)
                        processModulesDict[pm.Id] = pm;

                    foreach (var pem in characteristic.Value.ProcessExecutionModules)
                        processExecutionModulesDict[pem.Id] = pem;

                    foreach (var pems in characteristic.Value.ProcessExecutionModuleSpecs)
                        processExecutionModuleSpecsDict[pems.Id] = pems;
                }
            }
            tailoring.ProcessModules = processModulesDict.Values.ToArray();
            tailoring.ProcessExecutionModules = processExecutionModulesDict.Values.ToArray();

            //Add process execution module specifications from process execution modules
            foreach(var curPEM in tailoring.ProcessExecutionModules)
            {
                foreach(var curPEMS in curPEM.ProcessExecutionModuleSpecifications)
                    processExecutionModuleSpecsDict[curPEMS.Id] = curPEMS;
            }
            tailoring.ProcessExecutionModuleSpecifications = processExecutionModuleSpecsDict.Values.ToArray();
            //--------------------------

            return tailoring;
        }

        private static void CheckBuilderValidity(TailoringBuilder builder)
        {
            if (builder.ProjTypeVariant != null)
            {
                //Type variant
                bool foundPtv = false;
                foreach (var ptv in builder.ProjType.ProjectTypeVariants)
                {
                    if(ptv.Id.Equals(builder.ProjTypeVariant.Id))
                    {
                        foundPtv = true;
                        break;
                    }
                }
                if(!foundPtv)
                    throw new ArgumentException("The specified project type variant is not allowed for this project type.");

                //Characteristics
                foreach(var characteristic in builder.ProjCharacteristics)
                {
                    bool foundChar = false;
                    foreach(var ptChar in builder.ProjType.ProjectCharacteristics.Union(builder.ProjTypeVariant.ProjectCharacteristics))
                    {
                        if(ptChar.Id.Equals(characteristic.Key.Id))
                        {
                            foundChar = true;
                            break;
                        }
                    }
                    if(!foundChar)
                        throw new ArgumentException("The specified project characteristic '" + characteristic.Key.Name + "' is not allowed for this project type (variant).");

                    
                    bool foundCharVal = false;
                    foreach(var charVal in characteristic.Key.Values)
                    {
                        if(charVal.Id.Equals(characteristic.Value.Id))
                        {
                            foundCharVal = true;
                            break;
                        }
                    }
                    if(!foundCharVal)
                        throw new ArgumentException("The specified value for the project characteristic '" + characteristic.Key.Name + "' is not allowed.");
                }
            }
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        private Tailoring()
        {}

        const string namespaceXmlNs = "http://www.w3.org/2000/xmlns/";
        const string namespaceXi = "http://www.w3.org/2001/XInclude";
        const string namespaceXsi = "http://www.w3.org/2001/XMLSchema-instance";

        /// <summary>
        /// Writes a Projektassistent-compatible VMP project file for this tailoring.
        /// </summary>
        /// <param name="fileName"></param>
        public void SaveVmpProjectFile(string fileName)
        {
            //Create project document and root
            XmlDocument vmpDoc = new XmlDocument();

            //Create project node
            XmlNode projectRoot = vmpDoc.CreateElement("VMProjekt");
            vmpDoc.AppendChild(projectRoot);


            //Add root attributes
            var xIncludeAttr = vmpDoc.CreateAttribute("xmlns", "xi", namespaceXmlNs);
            xIncludeAttr.InnerText = namespaceXi;
            vmpDoc.DocumentElement.SetAttributeNode(xIncludeAttr);

            var schemaLocationAttr = vmpDoc.CreateAttribute("xsi", "noNamespaceSchemaLocation", namespaceXsi);
            schemaLocationAttr.InnerText = new Uri(Model.ProjectSchemaPath).AbsoluteUri;
            vmpDoc.DocumentElement.SetAttributeNode(schemaLocationAttr);

            var xsIncludeAttr = vmpDoc.CreateAttribute("xmlns", "xsi", namespaceXmlNs);
            xsIncludeAttr.Value = namespaceXsi;
            vmpDoc.DocumentElement.SetAttributeNode(xsIncludeAttr);


            //Create project information node
            XmlNode projectInformationNode = vmpDoc.CreateElement("ProjektInformation");
            projectRoot.AppendChild(projectInformationNode);

            var projectNameNode = vmpDoc.CreateElement("Bezeichnung");
            projectNameNode.InnerText = ProjectName;
            projectInformationNode.AppendChild(projectNameNode);

            projectInformationNode.AppendChild(vmpDoc.CreateElement("VorlagenExportVerzeichnis"));
            projectInformationNode.AppendChild(vmpDoc.CreateElement("VModellExportVerzeichnis"));
            projectInformationNode.AppendChild(vmpDoc.CreateElement("PlanExportVerzeichnis"));

            //Create tailoring information node
            XmlNode tailoringInformationNode = vmpDoc.CreateElement("TailoringInformation");
            projectRoot.AppendChild(tailoringInformationNode);

            //Project type
            AppendLinkNode(tailoringInformationNode, "PTRef", ProjType.Id);

            //Write project characteristics
            foreach (var curCharacteristics in ProjCharacteristics)
            {
                var curPmNode = vmpDoc.CreateElement("PM");
                AppendLinkNode(curPmNode, "PMRef", curCharacteristics.Key.Id);
                AppendLinkNode(curPmNode, "PMWertRef", curCharacteristics.Value.Id);
                curPmNode.AppendChild(vmpDoc.CreateElement("Begruendung"));
                tailoringInformationNode.AppendChild(curPmNode);
            }

            //Write process modules
            foreach (var curModule in ProcessModules)
                AppendLinkNode(tailoringInformationNode, "VBRef", curModule.Id);
            tailoringInformationNode.AppendChild(vmpDoc.CreateElement("VBBegruendung"));

            //Project type variant
            AppendLinkNode(tailoringInformationNode, "PTVRef", ProjTypeVariant.Id);
            tailoringInformationNode.AppendChild(vmpDoc.CreateElement("PTVBegruendung"));

            //Create planning information node
            projectRoot.AppendChild(vmpDoc.CreateElement("PlanungsInformation"));


            //Add template texts include
            var templatesIncludeNode = vmpDoc.CreateElement("xi", "include", namespaceXi);
            var templateIncludeHrefAttr = vmpDoc.CreateAttribute("href");
            templateIncludeHrefAttr.Value = new Uri(Model.TemplateTextsDocumentPath).AbsoluteUri;
            templatesIncludeNode.Attributes.Append(templateIncludeHrefAttr);
            projectRoot.AppendChild(templatesIncludeNode);


            //Save file
            using (XmlWriter vmpWriter = XmlWriter.Create(fileName))
            {
                vmpDoc.Save(vmpWriter);
            }
        }

        private static void AppendLinkNode(XmlNode parentNode, string nodeName, string val)
        {
            var node = parentNode.OwnerDocument.CreateElement(nodeName);
            var linkAttr = parentNode.OwnerDocument.CreateAttribute("link");
            linkAttr.Value = val;
            node.Attributes.Append(linkAttr);
            parentNode.AppendChild(node);
        }

        public void SaveProjectVModell(string destinationPath)
        {
            //Open model file
            XmlDocument modelDocument = new XmlDocument();
            modelDocument.Load(Model.ModelDocumentPath);


            #region Project types, variants and characteristics
            var projectTypesList = modelDocument.SelectNodes("V-Modell/V-Modellvariante/Projekttypen/Projekttyp");
            for (int a = 0; a < projectTypesList.Count; a++)
            {
                if (!projectTypesList[a].Attributes["id"].Value.Equals(ProjType.Id, StringComparison.InvariantCultureIgnoreCase))
                    projectTypesList[a].ParentNode.RemoveChild(projectTypesList[a]);
            }

            var projectTypeVariantsList = modelDocument.SelectNodes("V-Modell/V-Modellvariante/Projekttypvarianten/Projekttypvariante");
            for (int a = 0; a < projectTypeVariantsList.Count; a++)
            {
                if (!projectTypeVariantsList[a].Attributes["id"].Value.Equals(ProjTypeVariant.Id, StringComparison.InvariantCultureIgnoreCase))
                    projectTypeVariantsList[a].ParentNode.RemoveChild(projectTypeVariantsList[a]);
            }
            
            var projectCharactericsticsList = modelDocument.SelectNodes("V-Modell/V-Modellvariante/Projektmerkmale/Projektmerkmal");
            for (int a = 0; a < projectCharactericsticsList.Count; a++)
            {
                var curCharacteristicId = projectCharactericsticsList[a].Attributes["id"].Value;
                KeyValuePair<ProjectCharacteristic, ProjectCharacteristicValue>? curCharEntry = null;
                foreach(var curChar in ProjCharacteristics)
                {
                    if(curChar.Key.Id.Equals(curCharacteristicId, StringComparison.InvariantCultureIgnoreCase))
                    {
                        curCharEntry = curChar;
                        break;
                    }
                }
                if (curCharEntry.HasValue)
                {
                    //Remove unnecessary values
                    foreach (XmlNode curCharacteristicValueNode in projectCharactericsticsList[a].SelectNodes("Wert"))
                    {
                        if (!curCharacteristicValueNode.Attributes["id"].Value.Equals(curCharEntry.Value.Value.Id, StringComparison.InvariantCultureIgnoreCase))
                            curCharacteristicValueNode.ParentNode.RemoveChild(curCharacteristicValueNode);
                    }
                }
                else
                    projectCharactericsticsList[a].ParentNode.RemoveChild(projectCharactericsticsList[a]);
            }
            #endregion

            #region Process modules
            //Remove process modules, not needed
            var processModulesList = modelDocument.SelectNodes("V-Modell/V-Modellvariante/Vorgehensbausteine/Vorgehensbaustein");
            for (int a = 0; a < processModulesList.Count; a++)
            {
                var curModuleId = processModulesList[a].Attributes["id"].Value;
                var curModuleNeeded = false;
                foreach (var curNeededModule in ProcessModules)
                {
                    if (curNeededModule.Id.Equals(curModuleId, StringComparison.InvariantCultureIgnoreCase))
                    {
                        curModuleNeeded = true;
                        break;
                    }
                }

                if (!curModuleNeeded)
                {
                    //This module is not needed, remove it
                    processModulesList[a].ParentNode.RemoveChild(processModulesList[a]);
                }
            }
            //Get remaining process module nodes
            var remainingProcessModuleNodes = modelDocument.SelectNodes("V-Modell/V-Modellvariante/Vorgehensbausteine/Vorgehensbaustein"); 
            #endregion

            #region Process execution modules
            //Remove process execution modules, not needed
            var processExecutionModulesList = modelDocument.SelectNodes("V-Modell/V-Modellvariante/Ablaufbausteine/Ablaufbaustein");
            for (int a = 0; a < processExecutionModulesList.Count; a++)
            {
                var curModuleId = processExecutionModulesList[a].Attributes["id"].Value;
                var curModuleNeeded = false;
                foreach (var curNeededModule in ProcessExecutionModules)
                {
                    if (curNeededModule.Id.Equals(curModuleId, StringComparison.InvariantCultureIgnoreCase))
                    {
                        curModuleNeeded = true;
                        break;
                    }
                }

                if (!curModuleNeeded)
                {
                    //This module is not needed, remove it
                    processExecutionModulesList[a].ParentNode.RemoveChild(processExecutionModulesList[a]);
                }
            }
            #endregion

            #region Process execution module specifications
            //Remove process execution module specifications, not needed
            var processExecutionModuleSpecsList = modelDocument.SelectNodes("V-Modell/V-Modellvariante/Ablaufbausteinspezifikationen/Ablaufbausteinspezifikation");
            for (int a = 0; a < processExecutionModuleSpecsList.Count; a++)
            {
                var curModuleId = processExecutionModuleSpecsList[a].Attributes["id"].Value;
                var curModuleNeeded = false;
                foreach (var curNeededModule in ProcessExecutionModuleSpecifications)
                {
                    if (curNeededModule.Id.Equals(curModuleId, StringComparison.InvariantCultureIgnoreCase))
                    {
                        curModuleNeeded = true;
                        break;
                    }
                }

                if (!curModuleNeeded)
                {
                    //This module is not needed, remove it
                    processExecutionModuleSpecsList[a].ParentNode.RemoveChild(processExecutionModuleSpecsList[a]);
                }
            }
            #endregion

            #region Get needed process elements
            HashSet<string> neededTextBlocksSet = new HashSet<string>();
            HashSet<string> neededProductsSet = new HashSet<string>();
            HashSet<string> neededDecisionGatesSet = new HashSet<string>();
            HashSet<string> neededRolesSet = new HashSet<string>();
            HashSet<string> neededTopicsSet = new HashSet<string>();
            HashSet<string> neededActivitiesSet = new HashSet<string>();
            HashSet<string> neededTasksSet = new HashSet<string>();
            HashSet<string> neededDisciplinesSet = new HashSet<string>();
            HashSet<string> neededMethodReferencesSet = new HashSet<string>();
            HashSet<string> neededToolReferencesSet = new HashSet<string>();

            foreach (XmlNode curProcessModuleNode in remainingProcessModuleNodes)
            {
                //Get needed roles
                var roleAssociationsNode = curProcessModuleNode.SelectSingleNode("Beziehungen/Rollenbeziehungen");
                FetchElementLinks(roleAssociationsNode.SelectNodes("RolleVerantwortetProdukt/RolleRef"), neededRolesSet);

                foreach (XmlNode curInvolvedNode in roleAssociationsNode.SelectNodes("RolleWirktMitBeiProdukt"))
                {
                    //Note: Only involved roles that are mandatory are kept after the tailoring
                    var roleMandatoryNode = curInvolvedNode.SelectSingleNode("IstUnverzichtbar");
                    if (roleMandatoryNode != null && roleMandatoryNode.InnerText.Equals("Ja", StringComparison.InvariantCultureIgnoreCase))
                    {
                        var curRoleId = curInvolvedNode.SelectSingleNode("RolleRef/@link").Value.ToLower();
                        if (!neededRolesSet.Contains(curRoleId))
                            neededRolesSet.Add(curRoleId);
                    }
                }

                //Get product ids
                FetchElementIds(curProcessModuleNode, "Produkt", neededProductsSet);

                //Get needed text block IDs
                var neededTextBlocks = curProcessModuleNode.SelectNodes(".//TextbausteinRef");
                FetchElementLinks(neededTextBlocks, neededTextBlocksSet);

                //Get topic ids
                FetchElementIds(curProcessModuleNode, "Thema", neededTopicsSet);

                //Get discipline ids
                FetchElementIds(curProcessModuleNode, "Disziplin", neededDisciplinesSet);

                //Get activity and task ids
                FetchElementIds(curProcessModuleNode, "Aktivität", neededActivitiesSet);
                FetchElementIds(curProcessModuleNode, "Arbeitsschrittknoten", neededTasksSet);

                //Get needed method references
                var neededMethodRefRefs = modelDocument.SelectNodes(".//MethodenreferenzRef");
                FetchElementLinks(neededMethodRefRefs, neededMethodReferencesSet);

                //Get needed tool references
                var neededToolRefRefs = modelDocument.SelectNodes(".//WerkzeugreferenzRef");
                FetchElementLinks(neededToolRefRefs, neededToolReferencesSet);
            }

            //Get needed decision gates
            var neededDecisionGateRefs = modelDocument.SelectNodes("V-Modell/V-Modellvariante/Ablaufbausteine/Ablaufbaustein/Ablaufentscheidungspunkt/EntscheidungspunktRef");
            FetchElementLinks(neededDecisionGateRefs, neededDecisionGatesSet);
            #endregion

            #region Text blocks
            //Remove text blocks, not needed
            var textBlocksNodes = modelDocument.SelectNodes("V-Modell/V-Modellvariante/Textbausteine/Textbaustein");
            RemoveElementsNotInList(textBlocksNodes, neededTextBlocksSet);
            #endregion

            #region Roles
            //Remove roles, not needed
            var roleNodes = modelDocument.SelectNodes("V-Modell/V-Modellvariante/Rollen/Rolle");
            RemoveElementsNotInList(roleNodes, neededRolesSet);
            #endregion

            //Remove decision gates, not needed
            var decisionGateNodes = modelDocument.SelectNodes("V-Modell/V-Modellvariante/Entscheidungspunkte/Entscheidungspunkt");
            RemoveElementsNotInList(decisionGateNodes, neededDecisionGatesSet);

            #region Role associations, Product references
            foreach (XmlNode curProcessModuleNode in remainingProcessModuleNodes)
            {
                //Remove unnecessary role associations
                var roleAssociationsNode = curProcessModuleNode.SelectSingleNode("Beziehungen/Rollenbeziehungen");
                XmlNodeListDelegate removeUnnecessaryRoleNodes = delegate(XmlNodeList nodes)
                {
                    for (int a = 0; a < nodes.Count; a++)
                    {
                        var curRoleId = nodes[a].SelectSingleNode("RolleRef/@link").Value.ToLower();
                        if (!neededRolesSet.Contains(curRoleId))
                            roleAssociationsNode.RemoveChild(nodes[a]);
                    }
                };
                removeUnnecessaryRoleNodes(roleAssociationsNode.SelectNodes("RolleVerantwortetProdukt"));
                removeUnnecessaryRoleNodes(roleAssociationsNode.SelectNodes("RolleWirktMitBeiProdukt"));


                //Remove unnecessary product references
                var productRefs = curProcessModuleNode.SelectNodes("Beziehungen/Produktabhängigkeitsbeziehungen//ProduktRef");
                RemoveElementsByDanglingLink(productRefs, neededProductsSet);


                //Remove unnecessary decision gate references
                var productToDecisionGateNodes = curProcessModuleNode.SelectNodes("Beziehungen/Produktbeziehungen/ProduktZuEntscheidungspunkt");
                for (int a = 0; a < productToDecisionGateNodes.Count; a++)
                {
                    var curDecisionGateId = productToDecisionGateNodes[a].SelectSingleNode("EntscheidungspunktRef/@link").Value.ToLower();
                    if (!neededDecisionGatesSet.Contains(curDecisionGateId))
                        productToDecisionGateNodes[a].ParentNode.RemoveChild(productToDecisionGateNodes[a]);
                }

                //Remove unnecessary topic references
                var taskToTopicNodes = curProcessModuleNode.SelectNodes("Beziehungen/Aktivitätsbeziehungen/ArbeitsschrittknotenZuThema");
                for (int a = 0; a < taskToTopicNodes.Count; a++)
                {
                    var curTopicId = taskToTopicNodes[a].SelectSingleNode("ThemaRef/@link").Value.ToLower();
                    if (!neededTopicsSet.Contains(curTopicId))
                        taskToTopicNodes[a].ParentNode.RemoveChild(taskToTopicNodes[a]);
                }
            } 
            #endregion

            //Remove unnecessary method references
            var methodReferenceNodes = modelDocument.SelectNodes("V-Modell/V-Modellvariante/Methodenreferenzen/Methodenreferenz");
            RemoveElementsNotInList(methodReferenceNodes, neededMethodReferencesSet);
            
            //Remove unnecessary tool references
            var toolReferenceNodes = modelDocument.SelectNodes("V-Modell/V-Modellvariante/Werkzeugreferenzen/Werkzeugreferenz");
            RemoveElementsNotInList(toolReferenceNodes, neededToolReferencesSet);

            //Remove mappings
            RemoveElementsByDanglingLink(modelDocument, "SchnittstellenQuellproduktRef", neededProductsSet);
            RemoveElementsByDanglingLink(modelDocument, "wird_abgebildet_durchProduktRef", neededProductsSet);
            RemoveElementsByDanglingLink(modelDocument, "wird_abgebildet_durchThemaRef", neededTopicsSet);
            RemoveElementsByDanglingLink(modelDocument, "wird_abgebildet_durchRolleRef", neededRolesSet);
            RemoveElementsByDanglingLink(modelDocument, "wird_abgebildet_durchAktivitätRef", neededActivitiesSet);
            RemoveElementsByDanglingLink(modelDocument, "wird_abgebildet_durchArbeitsschrittRef", neededTasksSet);
            RemoveElementsByDanglingLink(modelDocument, "wird_abgebildet_durchDisziplinRef", neededDisciplinesSet);
            if (ProjTypeVariant != null)
                RemoveElementsByDanglingLink(modelDocument, "wird_abgebildet_durchPTVRef", new HashSet<string>(new[] { ProjTypeVariant.Id.ToLower() }));

            var neededProcessModulesSet = new HashSet<string>();
            foreach (var curPM in ProcessModules)
                neededProcessModulesSet.Add(curPM.Id.ToLower());
            RemoveElementsByDanglingLink(modelDocument, "wird_abgebildet_durchVBRef", neededProcessModulesSet);
            

            //Save model to temporary folder
            var tempModelPath = Path.GetTempFileName();
            modelDocument.Save(tempModelPath);


            //Create destination directory, copy necessary files and move model
            Directory.CreateDirectory(destinationPath);

            File.Copy(Model.XmlSchemaPath, destinationPath + "/" + VModellXT.XmlSchemaName, true);
            File.Copy(Model.ModelSchemaPath, destinationPath + "/" + VModellXT.ModelSchemaName, true);
            File.Copy(tempModelPath, destinationPath + "/" + VModellXT.ModelDocumentName, true);
            File.Delete(tempModelPath);
        }

        static void RemoveElementsByDanglingLink(XmlNode parentNode, string elementName, HashSet<string> linksSet)
        {
            foreach (XmlNode curElement in parentNode.SelectNodes("//" + elementName))
            {
                var link = (curElement.Attributes["link"].Value ?? string.Empty).ToLower();
                if (!linksSet.Contains(link))
                    curElement.ParentNode.RemoveChild(curElement);
            }
        }

        static void RemoveElementsByDanglingLink(XmlNodeList nodes, HashSet<string> linksSet)
        {
            foreach (XmlNode curElement in nodes)
            {
                var link = (curElement.Attributes["link"].Value ?? string.Empty).ToLower();
                if (!linksSet.Contains(link))
                    curElement.ParentNode.RemoveChild(curElement);
            }
        }

        static void RemoveElementsNotInList(XmlNodeList nodes, HashSet<string> elementIds)
        {
            for (int a = 0; a < nodes.Count; a++)
            {
                if (!elementIds.Contains(nodes[a].Attributes["id"].Value.ToLower()))
                    nodes[a].ParentNode.RemoveChild(nodes[a]);
            }
        }

        static void FetchElementIds(XmlNode parentNode, string elementName, HashSet<string> idSet)
        {
            foreach (XmlNode curNode in parentNode.SelectNodes("//" + elementName))
            {
                var curElementId = curNode.Attributes["id"].Value.ToLower();
                if (!idSet.Contains(curElementId))
                    idSet.Add(curElementId);
            }
        }

        static void FetchElementLinks(XmlNodeList nodes, HashSet<string> linkSet)
        {
            foreach (XmlNode curNode in nodes)
            {
                var curLink = curNode.Attributes["link"].Value.ToLower();
                if (!linkSet.Contains(curLink))
                    linkSet.Add(curLink);
            }
        }
    }
    delegate void XmlNodeListDelegate(XmlNodeList nodes);
}
